package com.jl15988.chainlog.manual;

import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

import java.util.Map;
import java.util.function.BiFunction;
import java.util.function.Function;

/**
 * 统一监控助手类
 * 提供手动监控和Lambda监控的静态方法
 */
//@Component
public class ChainLogHelper implements ApplicationContextAware {

    private static ApplicationContext applicationContext;
    private static ChainLogManual chainLogManual;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        ChainLogHelper.applicationContext = applicationContext;
        ChainLogHelper.chainLogManual = applicationContext.getBean(ChainLogManual.class);
    }

    /**
     * 开始监控方法
     *
     * @return 监控会话对象
     */
    public static ChainLogSession start() {
        return getManualMonitor().start(getCurrentMethodName());
    }

    /**
     * 开始监控方法
     *
     * @param name 监控名称
     * @return 监控会话对象
     */
    public static ChainLogSession start(String name) {
        return getManualMonitor().start(getCurrentMethodName(), name);
    }

    /**
     * 开始监控方法
     *
     * @param name 监控名称
     * @param desc 详细信息
     * @return 监控会话对象
     */
    public static ChainLogSession start(String name, String desc) {
        return getManualMonitor().start(getCurrentMethodName(), name, desc);
    }

    /**
     * 开始监控方法
     *
     * @param name    监控名称
     * @param desc    详细信息
     * @param customs 自定义参数
     * @return 监控会话对象
     */
    public static ChainLogSession start(String name, String desc, Map<String, String> customs) {
        return getManualMonitor().start(getCurrentMethodName(), name, desc, customs);
    }

    /**
     * 通过Spring代理调用无参数方法
     *
     * @param serviceClass 服务类Class
     * @param methodCall   方法调用Lambda
     * @param <T>          服务类型
     * @param <R>          返回值类型
     * @return 方法执行结果
     */
    public static <T, R> R call(Class<T> serviceClass, Function<T, R> methodCall) {
        T proxyBean = getApplicationContext().getBean(serviceClass);
        return methodCall.apply(proxyBean);
    }

    /**
     * 通过Spring代理调用单参数方法
     *
     * @param serviceClass 服务类Class
     * @param methodCall   方法调用Lambda
     * @param arg1         第一个参数
     * @param <T>          服务类型
     * @param <P1>         第一个参数类型
     * @param <R>          返回值类型
     * @return 方法执行结果
     */
    public static <T, P1, R> R call(Class<T> serviceClass, BiFunction<T, P1, R> methodCall, P1 arg1) {
        T proxyBean = getApplicationContext().getBean(serviceClass);
        return methodCall.apply(proxyBean, arg1);
    }

    /**
     * 通过Spring代理调用双参数方法
     *
     * @param serviceClass 服务类Class
     * @param methodCall   方法调用Lambda
     * @param arg1         第一个参数
     * @param arg2         第二个参数
     * @param <T>          服务类型
     * @param <P1>         第一个参数类型
     * @param <P2>         第二个参数类型
     * @param <R>          返回值类型
     * @return 方法执行结果
     */
    public static <T, P1, P2, R> R call(Class<T> serviceClass, TriFunction<T, P1, P2, R> methodCall, P1 arg1, P2 arg2) {
        T proxyBean = getApplicationContext().getBean(serviceClass);
        return methodCall.apply(proxyBean, arg1, arg2);
    }

    /**
     * 通过Spring代理调用三参数方法
     *
     * @param serviceClass 服务类Class
     * @param methodCall   方法调用Lambda
     * @param arg1         第一个参数
     * @param arg2         第二个参数
     * @param arg3         第三个参数
     * @param <T>          服务类型
     * @param <P1>         第一个参数类型
     * @param <P2>         第二个参数类型
     * @param <P3>         第三个参数类型
     * @param <R>          返回值类型
     * @return 方法执行结果
     */
    public static <T, P1, P2, P3, R> R call(Class<T> serviceClass, QuadFunction<T, P1, P2, P3, R> methodCall, P1 arg1, P2 arg2, P3 arg3) {
        T proxyBean = getApplicationContext().getBean(serviceClass);
        return methodCall.apply(proxyBean, arg1, arg2, arg3);
    }

    /**
     * 通过Spring代理调用四参数方法
     *
     * @param serviceClass 服务类Class
     * @param methodCall   方法调用Lambda
     * @param arg1         第一个参数
     * @param arg2         第二个参数
     * @param arg3         第三个参数
     * @param arg4         第四个参数
     * @param <T>          服务类型
     * @param <P1>         第一个参数类型
     * @param <P2>         第二个参数类型
     * @param <P3>         第三个参数类型
     * @param <P4>         第四个参数类型
     * @param <R>          返回值类型
     * @return 方法执行结果
     */
    public static <T, P1, P2, P3, P4, R> R call(Class<T> serviceClass, PentaFunction<T, P1, P2, P3, P4, R> methodCall, P1 arg1, P2 arg2, P3 arg3, P4 arg4) {
        T proxyBean = getApplicationContext().getBean(serviceClass);
        return methodCall.apply(proxyBean, arg1, arg2, arg3, arg4);
    }

    /**
     * 获取Spring代理Bean（用于手动获取代理对象）
     *
     * @param serviceClass 服务类Class
     * @param <T>          服务类型
     * @return Spring代理Bean
     */
    public static <T> T getService(Class<T> serviceClass) {
        return getApplicationContext().getBean(serviceClass);
    }

    /**
     * 获取Spring代理Bean（通过Bean名称）
     *
     * @param beanName     Bean名称
     * @param serviceClass 服务类Class
     * @param <T>          服务类型
     * @return Spring代理Bean
     */
    public static <T> T getService(String beanName, Class<T> serviceClass) {
        return getApplicationContext().getBean(beanName, serviceClass);
    }

    // ========== 私有方法 ==========

    /**
     * 获取当前方法名
     */
    private static String getCurrentMethodName() {
        StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
        // stackTrace[0] = Thread.getStackTrace()
        // stackTrace[1] = getCurrentMethodName()
        // stackTrace[2] = start()
        // stackTrace[3] = 调用start的方法
        if (stackTrace.length > 3) {
            StackTraceElement element = stackTrace[3];
            return element.getClassName() + "." + element.getMethodName();
        }
        return "unknown.method";
    }

    /**
     * 获取ManualMonitor实例
     */
    private static ChainLogManual getManualMonitor() {
        if (chainLogManual == null) {
            if (applicationContext != null) {
                chainLogManual = applicationContext.getBean(ChainLogManual.class);
            } else {
                throw new IllegalStateException("ManualMonitor not initialized. Make sure Spring context is loaded.");
            }
        }
        return chainLogManual;
    }

    /**
     * 获取ApplicationContext实例
     */
    private static ApplicationContext getApplicationContext() {
        if (applicationContext == null) {
            throw new IllegalStateException("ApplicationContext not initialized. Make sure Spring context is loaded.");
        }
        return applicationContext;
    }

    // ========== 自定义函数式接口 ==========

    /**
     * 三参数函数式接口
     */
    @FunctionalInterface
    public interface TriFunction<T, U, V, R> {
        R apply(T t, U u, V v);
    }

    /**
     * 四参数函数式接口
     */
    @FunctionalInterface
    public interface QuadFunction<T, U, V, W, R> {
        R apply(T t, U u, V v, W w);
    }

    /**
     * 五参数函数式接口
     */
    @FunctionalInterface
    public interface PentaFunction<T, U, V, W, X, R> {
        R apply(T t, U u, V v, W w, X x);
    }
}